#include <list>
#include <cstdlib>
#include <map>
#include <set>
#include <cstring>
#include <pthread.h>

#include "../include/contest_interface.h"
#include "../include/Index.h"
#include "../include/Iterator.h"
#include "../include/tx.h"
#include "../include/Mutex.h"
#include "../include/IndexSchema.h"
#include "../include/QuadTree.h"
#include "../include/util.h"

/*Index Scheme Functions*/
IndexSchema::IndexSchema(const char* name, uint8_t _attribute_count, KeyType _typ) {
    this->_name = name;
    dim = _attribute_count;
    _type = new AttributeType[dim];
    size_ = 0;
    qtree = new QuadTree(_attribute_count, _typ);
    // Build the size and copy the type array
    for (int i = 0; i < dim; i++) {
        if (_typ[i] == kShort)
            size_ += 4;
        else if (_typ[i] == kInt)
            size_ += 8;
        else
            size_ += MAX_VARCHAR_LENGTH + 1;

        _type[i] = _typ[i];
    }
}

IndexSchema::IndexSchema() {
    qtree = new QuadTree();
}

ErrorCode IndexSchema::executeTransaction(Transaction **tx) {
    std::vector< operation*>::iterator it_op;

    lock(mutex_) {
        try {
            ErrorCode ret;
            int length = (*tx)->Tx_operations()->size();
            it_op = (*tx)->Tx_operations()->begin();
            for (int i = 0; i < length; i++) {
                /*Only redo the operations that reported as successful (i.e. flagged as done)*/
                if ((*it_op)->done_flag) {
                    switch ((*it_op)->type) {
                        case 0: //Read Nothing
                            break;
                        case 1: // delete
                            try {
                                do {
                                    //                  CopyPayload((*it_op)->r.payload, (*it_op)->ptr->value()->payload);
                                    ret = this->get_QuadTree()->delete_Record((*it_op)->ptr->value(), 0);
                                    if (ret == kOk) {
                                        (*it_op)->count_success_op += 1;
                                        (*it_op)->dirty_flag = true;
                                    } else {
                                        break;
                                    }
                                } while ((*it_op)->ptr->next());

                                if (ret != kOk) {
                                    if ((*tx)->Tx_is_autoCommit())
                                        return ret;
                                    ret = Rollback(tx);
                                    return ret;
                                }
                                (*it_op)->dirty_flag = true;
                            } catch (ErrorCode err) {
                                ret = Rollback(tx);
                                return err;
                            } catch (std::bad_alloc&) {
                                ret = Rollback(tx);
                                return kErrorOutOfMemory;
                            } catch (...) {
                                ret = Rollback(tx);
                                return kErrorGenericFailure;
                            }

                            break;

                        case 2:
                            try {
                                ret = this->get_QuadTree()->insert(&((*it_op)->r));
                                if ((*tx)->Tx_is_autoCommit())
                                    return ret; //Modified by Basmah. if autoCOmmit, return ERrorCOde received as it is in case of INsert

                                if (ret != kOk) {
                                    ret = Rollback(tx); //For normal Tx, do rollback in case of failure for INsert OPeration
                                    return kTransactionAborted; // and return kTx aborted error
                                } else {
                                    (*it_op)->dirty_flag = true; //PUt in else by Basmah
                                    // For normal Tx, if it is kOk, put dirty true and continue doing other operations.
                                }
                            } catch (std::bad_alloc&) {
                                ret = Rollback(tx);
                                return kErrorOutOfMemory;
                            } catch (...) {
                                ret = Rollback(tx);
                                return kErrorGenericFailure;
                            }
                            //Call the Insert inside the quad Tree
                            break;
                        case 3:
                            try {
                                Record *temp;
                                do {
                                    //Swap old value with the new value so that the 
                                    //iterator points to the old value but in deferent space 
                                    CopyRecord(temp, (*it_op)->ptr->value());
                                    CopyPayload((*it_op)->r.payload, (*it_op)->ptr->value()->payload);
                                    (*it_op)->ptr->changeValue(temp);
                                    (*it_op)->dirty_flag = true;
                                    (*it_op)->count_success_op += 1;
                                } while ((*it_op)->ptr->next());

                                if (ret != kOk) {
                                    if ((*tx)->Tx_is_autoCommit())
                                        return ret;
                                    ret = Rollback(tx);
                                    return kTransactionAborted;
                                }
                                (*it_op)->dirty_flag = true;
                            } catch (ErrorCode err) {
                                ret = Rollback(tx);
                                return err;
                            } catch (std::bad_alloc&) {
                                ret = Rollback(tx);
                                return kErrorOutOfMemory;
                            } catch (...) {
                                ret = Rollback(tx);
                                return kErrorGenericFailure;
                            }

                            //Call the update inside the quad
                            break;
                    }
                }
                it_op++;
            }
        } catch (std::bad_alloc&) {
            return kErrorOutOfMemory;
        } catch (...) {
            return kErrorGenericFailure;
        }
    }
}

ErrorCode IndexSchema::Rollback(Transaction **tx) {
    //If done mark as dirty, swap the value between the pointer and the old Value
    //if sth wrong happened call rollback
    int size_ = (*tx)->Tx_operations()->size();
    if (size_ == 0)
        return kOk;
    std::vector< operation*>::iterator it_op;
    ErrorCode ret;
    (*tx)->Tx_set_Reject();
    it_op = (*tx)->Tx_operations()->begin();
    for (int i = 0; i < size_; i++) {
        if ((*it_op)->dirty_flag) {
            switch ((*it_op)->type) {
                case 0: //Read Nothing
                    break;
                case 1:
                    try {
                        uint8_t i = (*it_op)->count_success_op;
                        do {
                            ret = this->get_QuadTree()->insert((*it_op)->ptr->value());
                            if (ret != kOk)
                                break;
                            i--;
                            if (i < 1)
                                break;
                        } while ((*it_op)->ptr->next());
                        //
                        if (ret != kOk) {
                            return kErrorGenericFailure;
                        }
                        (*it_op)->dirty_flag = false;
                    } catch (std::bad_alloc&) {
                        return kErrorOutOfMemory;
                    } catch (...) {
                        return kErrorGenericFailure;
                    }

                    break;
                case 2:
                    try {
                        ret = this->get_QuadTree()->delete_Record(&(*it_op)->r, 0);
                        if (ret != kOk) {
                            if ((*tx)->Tx_is_autoCommit())
                                return ret;
                            ret = Rollback(tx);
                            return ret;
                        }
                        (*it_op)->dirty_flag = true;
                    } catch (ErrorCode err) {
                        return err;
                    } catch (std::bad_alloc&) {
                        return kErrorOutOfMemory;
                    } catch (...) {
                        return kErrorGenericFailure;
                    }
                    break;
                case 3:
                    try {
                        uint8_t i = (*it_op)->count_success_op;
                        do {
                            //TODO   
                            ret = this->get_QuadTree()->update_Record(&((*it_op)->r), &((*it_op)->ptr->value()->payload), 0); //insert((*it_op)->ptr->value());
                            if (ret != kOk)
                                break;
                            i--;
                            if (i < 1)
                                break;
                        } while ((*it_op)->ptr->next());
                        //
                        if (ret != kOk || i > 0) {
                            return kErrorGenericFailure;
                        }
                        (*it_op)->dirty_flag = false;
                    } catch (std::bad_alloc&) {
                        return kErrorOutOfMemory;
                    } catch (...) {
                        return kErrorGenericFailure;
                    }
                    break;
            }
        } else {
            continue;
        }
    }
}
// Register a new index handle

void IndexSchema::RegisterHandle(Index* hand) {

    lock(mutex_) {
        handles_.insert(hand);
    }
}

// Unregister an index handle

void IndexSchema::UnregisterHandle(Index* hand) {

    lock(mutex_) {
        handles_.erase(hand);
    }
}

// Close all registered handles

void IndexSchema::CloseHandles() {

    lock(mutex_) {
        std::set< Index*>::iterator it;
        it = handles_.begin();
        while (it != handles_.end()) {
            // Close Index handle
            if (*it != NULL)
                (*it)->Close(); //close the index
            it++;
        }
    }
}

bool IndexSchema::Compatible(Record *record) {
    if ((record == NULL)) // || !openFlag
        return false;
    if ((record->key.attribute_count != this->dim))
        return false;
    for (int i = 0; i < record->key.attribute_count; i++) {
        if (record->key.value[i]->type != this->_type[i])
            return false;
    }
    return true;
}

bool IndexSchema::Compatible(Key key) {
    if (key.attribute_count != this->dim)
        return false;
    for (int i = 0; i < key.attribute_count; i++) {
        if (key.value[i]->type != this->_type[i])
            return false;
    }
    return true;
}

// Start a new modifying transaction on this index
// This function returns false if the index is read-only (otherwise, true)

bool IndexSchema::HandleTransaction(Transaction *tx) {

    lock(mutex_) {
        transactions_.insert(tx);
        tx->Tx_set_asHandeled();
    }
    return true;
}

// End a modifying transaction on this index

ErrorCode IndexSchema::EndTransaction(Transaction *tx) {

    lock(mutex_) {
        transactions_.erase(tx);
        tx->Tx_set_asUnHandeled();
        tx = NULL;
    }
    return kOk;
}

